using Digitalmes.Infrastructure.Extensions;
using Digitalmes.Infrastructure.Jobs;
using Quartz;

namespace Digitalmes.Infrastructure;

/// <summary>
/// Job DI 注入。
/// </summary>
public static class JobSetup
{
    /// <summary>
    /// 注入 Quartz Job 服务。
    /// </summary>
    /// <param name="services"></param>
    /// <param name="configuration">配置服务</param>
    /// <param name="name">Quartz 配置名称，详细参考 <see cref="https://www.quartz-scheduler.net/documentation/quartz-3.x/configuration/reference.html"/></param>
    /// <returns></returns>
    public static IServiceCollection AddJobSetup(this IServiceCollection services, IConfiguration configuration, string name = "Quartz")
    {
        services.AddHttpClient(JobConstant.LocalHttpClient, client =>
        {
            var localhostUrl = configuration["Urls"]?.Split(";").FirstOrDefault() ?? string.Empty;
            client.BaseAddress = new Uri(localhostUrl);
        });

        services.Configure<QuartzOptions>(configuration.GetSection(name));
        var map = configuration.GetSection($"{name}:MyJobs").Get<Dictionary<string, string>>();

        services.AddQuartz(q =>
        {
            q.UseSimpleTypeLoader();
            q.UseInMemoryStore();
            q.UseDefaultThreadPool(tp => tp.MaxConcurrency = 10);
            q.UseTimeZoneConverter();

            var jobTypes = typeof(DependencyInjections).Assembly.GetAllAttributesOf<JobScheduleAttribute>();
            foreach (var jobType in jobTypes)
            {
                var attr = jobType.GetCustomAttribute<JobScheduleAttribute>()!;

                // 从配置文件中检索对应的 Cron，若存在优先使用配置文件的 Cron，若为null或空表示不启用此 Job。
                var cron = attr.Cron;
                if (map != null && map.TryGetValue(attr.Identity, out var cron1))
                {
                    // 为null或空表示不启用此 Job
                    if (string.IsNullOrEmpty(cron1))
                    {
                        continue;
                    }

                    cron = cron1;
                }

                // AddJob() + AddTrigger() 等同于 ScheduleJob<>()
                JobKey jobKey = new(attr.Identity, attr.Group);
                q.AddJob(jobType, jobKey);
                q.AddTrigger(trigger => trigger
                    .ForJob(jobKey)
                    .StartNow()
                    .WithCronSchedule(cron)
                    .WithDescription(attr.Description)
                );
            }
        });

        services.AddQuartzHostedService(options =>
        {
            // when shutting down we want jobs to complete gracefully
            options.WaitForJobsToComplete = true;
        });

        return services;
    }
}
